package com.bigknow.appstore.proxy.handler.base;

import com.bigknow.appstore.proxy.cache.ICacheManager;
import com.bigknow.appstore.proxy.cache.IProxyResourceMetaCache;
import com.bigknow.appstore.proxy.enumerate.ErrorType;
import com.bigknow.appstore.proxy.enumerate.ProxyState;
import com.bigknow.appstore.proxy.exceptions.ProxyError;
import com.bigknow.appstore.proxy.exceptions.processor.impl.ErrorAssert;
import com.bigknow.appstore.proxy.exceptions.processor.impl.ErrorHelper;
import com.bigknow.appstore.proxy.log.ILogWriter;
import com.bigknow.appstore.proxy.server.IProxyServer;
import com.bigknow.appstore.proxy.util.HandlerVariables;
import com.bigknow.appstore.proxy.vo.RequestVo;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import org.apache.poi.ss.formula.functions.Errortype;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;

import static com.bigknow.appstore.common.Assert.assertNotNull;

/**
 * Created by chaos on 2016/5/26.
 */
public abstract class BaseInboundHandler extends ChannelInboundHandlerAdapter {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    protected IProxyServer proxyServer;

    protected ICacheManager cacheManager;

    protected ILogWriter writer;

    public BaseInboundHandler(IProxyServer proxyServer, ICacheManager cacheManager, ILogWriter writer) {
        assertNotNull(cacheManager);
        assertNotNull(writer);
        this.cacheManager = cacheManager;
        this.writer = writer;
        this.proxyServer = proxyServer;
    }

    /**
     * msg前提条件是已经被HttpRequestDecoder处理过，msg类型应该是HttpRequest
     * 否则直接关闭连接
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        try {
            ErrorAssert.assertProxyServerState(ctx, proxyServer.getState());
            /**
             * 对于server端来说 fullHttpMessage就是 fullHttpRequest
             * 对于client端来说 fullHttpMessage就是 fullHttpResponse
             * 所以这里只能判断是否是fullHttpMsg，判断是否是正确的fullHttpRequest 或者 fullHttpResponse需要在
             * 具体实现类中判断
             */
            ErrorAssert.assertFullHttpMsg(ctx, msg, HandlerVariables.currentRequestVoLocal.get());
            this.read(ctx, (FullHttpMessage) msg);
        } catch (ProxyError proxyError) {
            //统一的错误处理
            proxyServer.getErrorProcessor().processError(ctx, proxyError);
        } catch (Exception e) {//如果出现了其他错误，就记录错误日志
            e.printStackTrace();
            try {
                if (msg instanceof FullHttpRequest) {
                    ErrorHelper.throwError(ctx.channel(), getCurrentRequestVo(), ErrorType.OTHER_ERROR, e.getMessage()
                            , msg.toString(), null);
                } else if (msg instanceof FullHttpResponse) {
                    ErrorHelper.throwError(ctx.channel(), getCurrentRequestVo(), ErrorType.OTHER_ERROR, e.getMessage()
                            , null, null);
                } else {
                    ErrorHelper.throwError(ctx.channel(), getCurrentRequestVo(), ErrorType.OTHER_ERROR, e.getMessage()
                            , null, null);
                }
            } catch (ProxyError proxyError) {
                if(proxyError.getRequestVo()==null){
                    String id = ((InetSocketAddress)ctx.channel().remoteAddress()).getAddress().getHostAddress();
                    proxyError.setRequestVo(HandlerVariables.lastRequestVos.get(id));
                }
                proxyServer.getErrorProcessor().processError(ctx, proxyError);
            }
        }

    }

    public IProxyResourceMetaCache getProxyResourceCache() {
        return cacheManager.getProxyResCache(proxyServer.getProxyId());
    }

    protected void closeConnection(Channel channel) {
        if (channel == null) return;
        channel.close().awaitUninterruptibly();
    }

    protected RequestVo getCurrentRequestVo() {
        return HandlerVariables.currentRequestVoLocal.get();
    }

    protected void setCurrentRequestVo(RequestVo requestVo){
        HandlerVariables.currentRequestVoLocal.set(requestVo);
    }

    public abstract void read(ChannelHandlerContext ctx, FullHttpMessage msg) throws Exception;
}
